-- 2022-8-18

---[[ 20.4.3 具有默认值的表

    -- 一个普通表中所有字段的默认值都是nil 通过元素 可以很容易的修改这个默认值
    function setDefault(t,d)
        local mt = {__index = function ()
            return d
        end}
        setmetatable(t,mt)
    end
    tab = {x=10,y=20}
    print(tab.x,tab.z)
    setDefault(tab,0)
    print(tab.x,tab.z)
    -- 在调用setDefault后 任何对表tab中不存在字段的访问都将调用它的__index元方法 而这个元方法会返回零(这个元方法中的值是d)

    -- 函数setDefault为所有需要默认值的表创建了一个新的闭包和一个新的元表 如果我们有很多需要默认值的表 那么开销会比较大
    -- 然而 由于具有默认值d的元表是与元方法关联在一起的 所以我们不能把同一个元表用于具有不同默认值的表
    -- 为了能够使所有的表都使用同一个元素 可以使用一个额外的字段将每个表的默认值存放到表自身中
    -- 如果不担心命名冲突的话 我们可以使用形如"___"这样的键作为额外的字段:
    local mt = {__index = function (t)
        return t.___
    end}
    function setDefault(t,d)
        t.___ = d
        setmetatable(t,mt)
    end
    -- 请注意 这里我们只在setDefault外创建了一次元表mt及对应的元方法

    -- 如果担心命名冲突 要确保这个特殊键的唯一性也很容易 只需要创建一个新的排除表 然后将它作为键即可
    local key = {} -- 唯一的键
    local mt = {__index = function (t)
        return t[key]
    end}
    function setDefault(t,d)
        t[key] = d
        setmetatable(t,mt)
    end

    -- 还有一种方法可以将每个表与其默认值关联起来 称为对偶表示(dual representation) 即使用一个独立的表 该表的键为各种表 值为这些表的默认值
    -- 不过 为了正确地实现这种做法 我们还需要一种特殊的表 称为弱引用(weak table)
    -- 在这里 我们暂时不会使用弱引用表 而在23章中再讨论这个话题

    -- 另一种为具有相同的默认值的表复用同一个元表的方式是记忆(memorize)元表
    -- 不过 这也需要用到弱引用表 我们会在第23章中继续学习
--]]